package com.fulu.game.bi.service.impl.auth;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.SecureUtil;
import com.fulu.game.bi.common.Constants;
import com.fulu.game.bi.common.exception.BizException;
import com.fulu.game.bi.common.util.lang.StringUtils;
import com.fulu.game.bi.entity.enums.GroupRelTableEnum;
import com.fulu.game.bi.entity.enums.StatusEnum;
import com.fulu.game.bi.entity.po.auth.BiPermission;
import com.fulu.game.bi.entity.po.auth.BiRole;
import com.fulu.game.bi.entity.po.auth.BiUser;
import com.fulu.game.bi.entity.po.channel.GroupRel;
import com.fulu.game.bi.entity.vo.auth.role.RoleQueryVO;
import com.fulu.game.bi.entity.vo.auth.user.UserQueryVO;
import com.fulu.game.bi.entity.vo.auth.user.UserSaveVO;
import com.fulu.game.bi.enums.CommonlyOrderEnums;
import com.fulu.game.bi.mysql.bi.auth.BiPermissionDao;
import com.fulu.game.bi.mysql.bi.auth.BiRoleDao;
import com.fulu.game.bi.mysql.bi.auth.BiUserDao;
import com.fulu.game.bi.mysql.bi.channel.GroupRelDao;
import com.fulu.game.bi.redis.service.auth.BiRoleService;
import com.fulu.game.bi.redis.service.auth.BiUserService;
import com.fulu.game.bi.service.impl.auth.config.BiSubject;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Param;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.subject.WebSubject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class UserServiceImpl implements BiUserService {

    @Autowired
    private BiUserDao biUserDao;

    @Autowired
    private GroupRelDao groupRelDao;

    @Autowired
    private BiRoleDao biRoleDao;

    @Autowired
    private BiPermissionDao biPermissionDao;

    @Override
    public BiUser getCurrentUser() {
        BiSubject subject = (BiSubject) SecurityUtils.getSubject();
        return subject.getCurrentUser();
    }

    @Override
    public BiUser findByUserName(String userName) {
        return biUserDao.findByUsername(userName);
    }

    @Override
    public Set<String> getRoles(String username) {
        List<BiRole> biRoles = getBiRoles(username);

        return biRoles.stream()
                .map(BiRole::getName)
                .collect(Collectors.toSet());
    }

    private List<BiRole> getBiRoles(String username) {
        BiUser user = findByUserName(username);
        List<BiRole> biRoles = Optional.ofNullable(user)
                .map(BiUser::getId)
                .map(userId -> biRoleDao.findByUserId(userId))
                .orElse(Collections.emptyList());
        return biRoles;
    }

    @Override
    public Set<String> getPermissions(String username) {
        Set<Integer> roles = getBiRoles(username).stream()
                .map(BiRole::getId)
                .collect(Collectors.toSet());
        if (CollectionUtil.isEmpty(roles)) {
            return new HashSet<>();
        }

        return biPermissionDao.findByRoleList(roles).stream()
                .map(BiPermission::getPath)
                .collect(Collectors.toSet());
    }

    @Override
    public PageInfo<BiUser> list(UserQueryVO vo) {
        PageHelper.startPage(vo.getPageNum(), vo.getPageSize(), CommonlyOrderEnums.UPDATE_DESC.getValue());
        List<BiUser> list = biUserDao.list(vo);

        PageInfo<BiUser> page = new PageInfo<>(list);

        page.getList().forEach(biUser ->  {
            List<BiRole> roles = getBiRoles(biUser.getUsername());
            if (CollectionUtil.isEmpty(roles)) {
                return;
            }
            BiRole biRole = roles.get(0);
            biUser.setRoleId(biRole.getId());
            biUser.setRoleName(biRole.getName());
        });

        return page;
    }

    @Override
    public BiUser save(UserSaveVO vo) {
        if (Objects.isNull(vo.getId())) {
            return createNewUser(vo);
        }

        return updateExistUser(vo);
    }

    private BiUser updateExistUser(UserSaveVO vo) {
        BiUser user = new BiUser();
        BeanUtils.copyProperties(vo, user);
        if (StringUtils.isNotEmpty(vo.getPassword())) {
            String salt = RandomUtil.randomString(32);
            String password = SecureUtil.md5(vo.getPassword() + salt);
            user.setPassword(password);
            user.setSalt(salt);
        }

        int res = biUserDao.updateSelective(user);

        updateUserRoleRel(vo.getRoleId(), user.getId());

        log.info("修改已有用户成功， 返回值 - {}", res);

        return user;
    }

    private void updateUserRoleRel(Integer roleId, Integer userId) {
        if (Objects.nonNull(roleId)) {
            groupRelDao.deleteByGroupId(userId, GroupRelTableEnum.USER_ROLE.getName());

            GroupRel groupRel = new GroupRel();
            groupRel.setGroupId(userId);
            groupRel.setRelId(roleId);

            groupRelDao.insert(Collections.singletonList(groupRel), GroupRelTableEnum.USER_ROLE.getName());
        }
    }

    private BiUser createNewUser(UserSaveVO vo) {
        BiUser existName = findByUserName(vo.getUsername());
        if (Objects.nonNull(existName)) {
            throw new BizException("用户名已存在");
        }

        BiUser user = new BiUser();
        String salt = RandomUtil.randomString(32);
        String password = SecureUtil.md5(vo.getPassword() + salt);

        BeanUtils.copyProperties(vo, user);
        Date createTime = new Date();

        user.setSalt(salt);
        user.setPassword(password);
        user.setStatus(StatusEnum.YES.getStatus());
        user.setIsDel(StatusEnum.NO.getStatus());
        user.setImId(Constants.UNKNOWN_WORD);
        user.setImPwd(Constants.UNKNOWN_WORD);
        user.setCreateTime(createTime);
        user.setUpdateTime(createTime);

        int res = biUserDao.insert(user);

        updateUserRoleRel(vo.getRoleId(), user.getId());

        log.info("新增用户: {}", res);

        return user;
    }

    @Override
    public void deleteById(Integer id) {
        biUserDao.deleteById(id);
        groupRelDao.deleteByGroupId(id, GroupRelTableEnum.USER_ROLE.getName());
    }

    @Override
    public BiUser findById(Integer adminId) {
        return biUserDao.findById(adminId);
    }

    @Override
    public void bindRole(Integer userId, List<Integer> roleIdList) {
        List<GroupRel> list = roleIdList.stream()
                .map(roleId -> {
                    GroupRel groupRel = new GroupRel();
                    groupRel.setGroupId(userId);
                    groupRel.setRelId(roleId);
                    return groupRel;
                })
                .collect(Collectors.toList());

        groupRelDao.insert(list, GroupRelTableEnum.USER_ROLE.getName());
    }
}
